#include "sd_io.h"
#include "stm32f1xx_hal.h"
#include "stm32f1xx_bsp_sd.h"
#include "spi.h"

#define SD_CS_HIGH()  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET)
#define SD_CS_LOW()  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET)

#define SD_DUMMY_BYTE   0xff

/******************************* SPI Routines**********************************/
static void SPIx_Error (void);
uint32_t SpixTimeout = 1000;        /*<! Value of Timeout when SPI communication fails */

/**
  * @brief SPI Read 4 bytes from device
  * @retval Read data
*/
static uint32_t SPIx_Read(void)
{
    HAL_StatusTypeDef status = HAL_OK;
    uint32_t          readvalue = 0;
    uint32_t          writevalue = 0xFFFFFFFF;

    status = HAL_SPI_TransmitReceive(&hspi1, (uint8_t *) &writevalue, (uint8_t *) &readvalue, 1, SpixTimeout);

    /* Check the communication status */
    if(status != HAL_OK)
    {
        /* Execute user timeout callback */
        SPIx_Error();
    }

    return readvalue;
}

/**
  * @brief  SPI Write a byte to device
  * @param  DataIn: value to be written
  * @param  DataOut: value to be read
  * @param  DataLength: length of data
  */
static void SPIx_WriteReadData(const uint8_t *DataIn, uint8_t *DataOut, uint16_t DataLength)
{
    HAL_StatusTypeDef status = HAL_OK;

    status = HAL_SPI_TransmitReceive(&hspi1, (uint8_t *) DataIn, DataOut, DataLength, SpixTimeout);

    /* Check the communication status */
    if(status != HAL_OK)
    {
        /* Execute user timeout callback */
        SPIx_Error();
    }
}

/**
  * @brief  SPI Write Data to device
  * @param  Data: value to be written
  * @param  DataLength: length of data
  */
static void SPIx_WriteData(const uint8_t *Data, uint16_t DataLength)
{
    HAL_StatusTypeDef status = HAL_OK;

    status = HAL_SPI_Transmit(&hspi1, (uint8_t *) Data, DataLength, SpixTimeout);

    /* Check the communication status */
    if(status != HAL_OK)
    {
        /* Execute user timeout callback */
        SPIx_Error();
    }
}

/**
  * @brief  SPI Read Data from device
  * @param  Data: value to be read
  * @param  DataLength: length of data
  */
static void SPIx_ReadData(const uint8_t *Data, uint16_t DataLength)
{
    HAL_StatusTypeDef status = HAL_OK;

    status = HAL_SPI_Receive(&hspi1, (uint8_t *) Data, DataLength, SpixTimeout);

    /* Check the communication status */
    if(status != HAL_OK)
    {
        /* Execute user timeout callback */
        SPIx_Error();
    }
}

/**
  * @brief  SPI Write a byte to device.
  * @param  Value: value to be written
  */
static void SPIx_Write(uint8_t Value)
{
    HAL_StatusTypeDef status = HAL_OK;
    uint8_t data;

    status = HAL_SPI_TransmitReceive(&hspi1, (uint8_t *) &Value, &data, 1, SpixTimeout);

    /* Check the communication status */
    if(status != HAL_OK)
    {
        /* Execute user timeout callback */
        SPIx_Error();
    }
}

/**
  * @brief  SPI error treatment function
  * @retval None
  */
static void SPIx_Error (void)
{
    /* De-initialize the SPI communication BUS */
    HAL_SPI_DeInit(&hspi1);

    /* Re-Initiaize the SPI communication BUS */
    MX_SPI1_Init();
}



/******************************** LINK SD Card ********************************/

/**
  * @brief  Initializes the SD Card and put it into StandBy State (Ready for
  *         data transfer).
  */
void SD_IO_Init(void)
{
    uint16_t counter;

    /*------------Put SD in SPI mode--------------*/
    /* SD SPI Config */
    MX_SPI1_Init();

    /* SD chip select high */
    SD_CS_HIGH();

    /* Send dummy byte 0xFF, 10 times with CS high */
    /* Rise CS and MOSI for 80 clocks cycles */
    for (counter = 0; counter <= 9; counter++)
    {
        /* Send dummy byte 0xFF */
        SD_IO_WriteByte(SD_DUMMY_BYTE);
    }
}

/**
  * @brief  Set the SD_CS pin.
  * @param  pin value.
  * @retval None
  */
void SD_IO_CSState(uint8_t val)
{
    if(val == 1)
    {
        SD_CS_HIGH();
    }
    else
    {
        SD_CS_LOW();
    }
}

/**
  * @brief Write a byte on the SD.
  * @param  DataIn: value to be written
  * @param  DataOut: value to be read
  * @param  DataLength: length of data
  */
void SD_IO_WriteReadData(const uint8_t *DataIn, uint8_t *DataOut, uint16_t DataLength)
{
    /* Send the byte */
    SPIx_WriteReadData(DataIn, DataOut, DataLength);
}

/**
  * @brief  Write a byte on the SD.
  * @param  Data: value to be written
  * @param  DataLength: length of data
  */
void SD_IO_WriteData(const uint8_t *Data, uint16_t DataLength)
{
    /* Send the byte */
    SPIx_WriteData(Data, DataLength);
}

/**
  * @brief  Read a byte from the SD.
  * @param  Data: value to be read
  * @param  DataLength: length of data
  */
void SD_IO_ReadData(const uint8_t *Data, uint16_t DataLength)
{
    /* Send the byte */
    SPIx_ReadData(Data, DataLength);
}

/**
  * @brief  Writes a byte on the SD.
  * @param  Data: byte to send.
  */
uint8_t SD_IO_WriteByte(uint8_t Data)
{
    uint8_t tmp;
    /* Send the byte */
    SPIx_WriteReadData(&Data, &tmp, 1);
    return tmp;
}

